#ifndef P3_ERROR_H
#define P3_ERROR_H

#include <sstream>

// Context info added to output.
// This is overridden with the context information
// set via SetContext() if that is non-empty.
#define CONTEXT_   __FUNCTION__

// Declaration of user symbols with stream syntax (must be macros to include debug info):
#define p3err      p3err__( CONTEXT_ )
#define p3out      p3out__( CONTEXT_ )
#define p3dbg      p3dbg__( CONTEXT_ )
#define p3err_     p3err__( typeid( *this ).name() )
#define p3out_     p3out__( typeid( *this ).name() )


namespace P3Error
{
  // Stream message handling actions for p3err/p3out.
  void DebugMessage( const std::string& );
  //void PlainMessage( const std::string& );
  void Warning( const std::string& );
  void ConfigurationError( const std::string& );
  void RuntimeError( const std::string& );
  void LogicError( const std::string& );

  class OutStream : public std::ostream
  {
   public:
    OutStream()
      : std::ostream( 0 )
      { this->init( &mBuf ); }

    OutStream& operator()( const char* );
    OutStream& operator()(); // in case CONTEXT_ is defined empty
    // Specifying a debug level with a message will result in filtering out all
    // messages above the global debug level variable.
    //OutStream& operator()( int debugLevel )
    //  { return Debug( debugLevel ); }
    //OutStream& Debug( int debugLevel );

    int Flushes()
      { return mBuf.Flushes(); }
    void Clear()
      { clear(); }
    void clear()
      { std::ostream::clear(); mBuf.Clear(); }
    void Reset()
      { std::ostream::clear(); mBuf.Reset(); }

   private:
    static void SetContext( const std::string& s )
      { sContext = s; }
    //static void SetDebugLevel( int i )
    //  { sDebugLevel = i; }

    typedef void ( *FlushHandler )( const std::string& );
    FlushHandler SetFlushHandler( FlushHandler f = NULL )
      { return mBuf.SetFlushHandler( f ); }

    class StringBuf : public std::stringbuf
    {
     public:
      StringBuf()
      : std::stringbuf( std::ios_base::out ),
        mpOnFlush( LogicError ),
        mNumFlushes( 0 )
      {}

      void SetContext( const std::string& s )
        { mContext = s; }
      FlushHandler SetFlushHandler( FlushHandler f = NULL );
      int Flushes()
        { return mNumFlushes; }
      void Clear()
        { clear(); }
      void clear()
        { SetFlushHandler( mpOnFlush ); mNumFlushes = 0; }
      void Reset()
        { str( "" ); mNumFlushes = 0; }

     private:
      FlushHandler mpOnFlush;
      int          mNumFlushes;
      std::string  mContext;

     protected:
      // This function gets called on ostream::flush().
      virtual int   sync();
    } mBuf;

    static std::string sContext;
    //static int         sDebugLevel; // global debug level
  };

}

extern P3Error::OutStream p3err__;
extern P3Error::OutStream p3out__;
extern P3Error::OutStream p3dbg__;

#endif // P3_ERROR_H

